import { isHTMLTag, makeMap,isUndef, isDef } from '../util/index'


function render(el) {
    if (el.nodeType === 1) {
        const tag = el.tagName.toLowerCase()
        if (tag === 'slot') { //插槽标签(插槽标签不解析其子标签)
            let renderStr = ''
            renderStr = '_t('
            renderStr += (el.getAttribute('name') !== null ? `"${el.getAttribute('name')}",` : '"default",')
            renderStr += parseBindAttr(el.getAttributeNames().reduce((init, key) => {
                const value = el.getAttribute(key)
                if (key !== 'name') {
                    init[key] = value
                }
                return init
            }, {}))
            renderStr += ')'
            return renderStr
        } else { //其它标签
            //template标签的子类在content里面
            const childNodes = tag === 'template' ? el.content.childNodes : el.childNodes
            let children = []
            let templateChild = []
            let isForArray = false
            if (childNodes.length > 0) {
                for (let index = 0; index < childNodes.length; index++) {
                    const element = childNodes[index];
                    let temChildStr = ''
                    let tagClid
                    if (element.nodeType === 1) { //DOM子类
                        tagClid = element.tagName.toLowerCase()
                        //v-for和v-if互斥，不能在同一元素上
                        if (element.getAttribute('v-for') !== null) {
                            isForArray = true
                            temChildStr = renderList(element) //存在for
                        } else {

                            const ifVal = element.getAttribute('v-if')
                            if (tagClid === 'slot') {
                                isForArray = true
                            }
                            if (ifVal !== null) {
                                let j = index + 1
                                let lastConfition = ''
                                temChildStr = '(' + ifVal + ')?' + render(element) + ':'
                                for (; j < childNodes.length; j++) {
                                    const elementJ = childNodes[j];
                                    if (elementJ.nodeType === 1) { //条件语句之间的文本节点直接跳过不解析
                                        let elseIfVal = elementJ.getAttribute('v-else-if')
                                        let elseVal = elementJ.getAttribute('v-else')
                                        if (elseIfVal !== null) {
                                            lastConfition = 'v-else-if'
                                            temChildStr += '(' + elseIfVal + ')?' + render(elementJ) + ':'
                                        } else if (elseVal !== null) {
                                            lastConfition = 'v-else'
                                            temChildStr += render(elementJ)
                                        } else {
                                            //什么都解析不到
                                            break
                                        }
                                    }
                                }
                                if (!lastConfition) { //说明没有解析到else if 或者else
                                    temChildStr += '_e("")'
                                } else {
                                    if (lastConfition === 'v-else-if') {//else if就结尾了
                                        temChildStr += '_e("")'
                                    }
                                    index = j + 1
                                }
                            } else {
                                temChildStr = render(element)
                            }
                        }
                    } else {
                        //文本子类
                        temChildStr = render(element)
                    }
                    
                    if (temChildStr ===  '_v("")') {
                        //匹配到了空的文本子类，这里什么也不做
                     
                    } else if (tagClid === 'template') {
                        let beginDom = (element.outerHTML.slice(0, element.outerHTML.indexOf('>')))
                        let hasName = beginDom.indexOf('v-slot:') > -1 || beginDom.indexOf('#') > -1
                        if (hasName) {
                            templateChild.push(temChildStr)
                        } else {
                            isForArray = true
                            children.push(temChildStr)
                        }
                    } else {
                        children.push(temChildStr)
                    }
                }
            }


            //当前是template标签
            if (tag === 'template') {
                let scopeNameAttr = ''
                let renderStr = ''
                //查看是否存在#name属性，有#name属性的子类将会作为命名插槽的内容
                el.getAttributeNames().some((item) => {
                    if (item.indexOf('#') > -1) {
                        scopeNameAttr = {
                            key: item.split('#')[1],
                            val: el.getAttribute(item) || ''
                        }
                        return true
                    }
                })
                if (scopeNameAttr) {
                    //如果命名template里面还存在命名template,直接丢弃，这里取普通元素
                    renderStr += `{ key: "${scopeNameAttr.key}", fn: function(${scopeNameAttr.val}){ return [ ${children.join(',')} ]  }}`
                } else {
                    renderStr += `[ ${children.join(',')} ] `
                }
                return renderStr
            } else if (tag === 'component') {
                let renderStr = ''
                const bindIs = el.getAttribute(':is')
                const staticIs = el.getAttribute('is')
                renderStr += '_c('
                let hasIs = true
                if(staticIs){
                    renderStr += `"${staticIs}"`
                }else if(bindIs){
                    renderStr += parseExp(bindIs)
                }else{
                    hasIs = false
                    console.warn('component标签没有提供is属性');
                }
                if(hasIs){
                    renderStr += `,{ tag: "${tag}" }`
                    renderStr += `,[ ${children.join(',')} ] `
                }
                renderStr += ')'
                return renderStr
            } else {
                let renderStr = ''
                //当前标签的子类存在template标签的命名插槽内容
                if (templateChild.length > 0) {
                    //将解析到的命名插槽子类存放到el中给parseAttributes方法解析
                    el.setAttribute('scopedSlots', '_u([' + templateChild.join(',') + '])')
                }
                renderStr += '_c('
                renderStr += `"${tag}"`
                renderStr += ',' + parseAttributes(el)
                renderStr += ',[' + children.join(',') + ']'  //子类
                renderStr += isForArray ? ',2' : '' //解析方式
                renderStr += ')' //结束
                return renderStr
            }
        }

    } else if (el.nodeType === 3) {
        return `_v(${parseText(el.textContent)})`
    }
}

function renderList(element) {
    let renderStr = ''
    let forData = parseFor(element.getAttribute('v-for'))
    renderStr += '_l('
    renderStr += forData.for
    renderStr += ',function('
    renderStr += forData.alias
    if(isDef(forData.iterator1)){
        renderStr += ( ',' + forData.iterator1 )
    }
    renderStr += '){ return ' + render(element) + '})'
    return renderStr
}


function parseFor(exp) {
    const forAliasRE = /([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/
    const stripParensRE = /^\(|\)$/g
    const forIteratorRE = /,([^,\}\]]*)(?:,([^,\}\]]*))?$/
    const inMatch = exp.match(forAliasRE)
    if (!inMatch) return
    const res = {}
    res.for = inMatch[2].trim()
    const alias = inMatch[1].trim().replace(stripParensRE, '')
    const iteratorMatch = alias.match(forIteratorRE)
    if (iteratorMatch) {
        res.alias = alias.replace(forIteratorRE, '').trim()
        res.iterator1 = iteratorMatch[1].trim()
        if (iteratorMatch[2]) {
            res.iterator2 = iteratorMatch[2].trim()
        }
    } else {
        res.alias = alias
    }
    return res
}

function parseText(text) {
    var tagRE = /\{\{((?:.|\r?\n)+?)\}\}/
    if (!tagRE.test(text)) {
        return JSON.stringify(text.trim())
    }
    var tokens = [];
    var lastIndex = tagRE.lastIndex = 0;
    var match, index
    while ((match = tagRE.exec(text))) {
        index = match.index;
        if (index > lastIndex) {
            tokens.push(JSON.stringify(text.slice(lastIndex, index)));
        }
        tokens.push(("_s(" + parseFilter(match[1]) + ")"));
        text = text.replace(match[0].trim(), '')
    }
    return tokens.join('+')
}
function parseFilter(text) {
    let splitText = text.split(' | ')
    const _variable = splitText[0]
    //大于1说明存在filter
    if (splitText.length > 1) {
        const baseParams = splitText.shift()
        return filterCycle(splitText, baseParams)
    } else {
        return _variable
    }
}

function filterCycle(filterArr = [], baseParams){
    //数组反向解析
    let filter = filterArr.pop()
    let params = filter.match(/(?:\()(.+)(?:\))/)
    let renderStr = ''
    if(params){
        renderStr += `_f("${filter.replace(params[0],'')}")(` //get filter name
        if(filterArr.length > 0){
            renderStr+= filterCycle(filterArr,baseParams) + ')'
        }else{ //当前是最后或者只有一个filter
            renderStr += `${baseParams},${params[1]})`
        }
    }else{
        renderStr += `_f("${filter}")(`
        if(filterArr.length > 0){
            renderStr+= filterCycle(filterArr,baseParams) + ')'
        }else{ //当前是最后或者只有一个filter
            renderStr += `${baseParams})`
        }
    }
    return renderStr
}

function parseAttributes(el) {
    let renderStr = '{'
    const attr = {}
    const on = {}
    const directives = []
    const keys = el.getAttributeNames()
    keys.map((item) => {
        const value = el.getAttribute(item)
        if (item === 'class') { //静态class
            renderStr += `staticClass: "${value}",`
        } else if (item === 'style') { //静态style
            renderStr += `staticStyle: "${value}",`
        } else if (item === ':class') { //动态class
            renderStr += `class: ${value},`
        } else if (item === ':style') { //动态style
            renderStr += `style: ${value},`
        } else if (item === 'scopedslots') { //命名插槽内容
            renderStr += `scopedSlots: ${value},`
        } else if (item === ':key') { //for循环唯一key
            renderStr += `key: ${value},`
        } else if (item.indexOf('@') > -1) { //自定义事件
            const params = value.match(/(?:\()(.+)(?:\))/)
            const eventVal = params?`function($event){ return ${value} }`:value
            on[item] = eventVal
        } else if (item === 'ref') { //ref引用
            renderStr += `ref: "${value}",`
        } else if (item === 'v-model') { //v-model指令
            const tagName = el.tagName.toLowerCase()
            //input, textarea, select , 组件 都可以实现v-model
            if (tagName === 'input' || tagName === 'textarea' || tagName === 'select') {
                renderStr += `domProps: ${parseExp(value)} ,`
                on['@input'] = `function($event){ ${parseExp(value)}  =$event.target.value }`
            } else if (!isHTMLTag(tagName)) {
                attr[':value'] = parseExp(value)
                on['@input'] = `function($event){ ${parseExp(value)}  =$event }`
            }
        } else if (item.indexOf('v-') > -1 && !isBuiltInDirective(item)) {//自定义指令
            directives.push({
                'name': item.replace('v-', ''),
                'rawName': item,
                ':value': parseExp(value),
                'expression': value
            })
        } else { //其它属性
            attr[item] = value
        }
    })
    if (Object.keys(attr).length > 0) {
        renderStr += 'attrs: ' + parseBindAttr(attr) + ','
    }
    if (Object.keys(on).length > 0) {
        renderStr += 'on: ' + parseBindAttr(on) + ','
    }
    if (directives.length > 0) {
        renderStr += 'directives: ' + parseBindAttr(directives) + ','
    }

    if (renderStr.charAt(renderStr.length - 1) === ',') {
        renderStr = renderStr.slice(0, renderStr.length - 1)
    }
    renderStr += '}'
    return renderStr
}

//解析动态属性
function parseBindAttr(attr) {
    if (Array.isArray(attr)) {
        let renderStr = '['
        for (const item of attr) {
            renderStr += (parseBindAttr(item) + ',')
        }
        renderStr += ']'
        return renderStr
    } else if (typeof attr === 'object') {
        let renderStr = '{'
        for (const key in attr) {
            const val = attr[key];
            if (key.indexOf(':') > -1) { //动态属性
                renderStr += `"${key.replace(':', '')}": ${val},`
            } else if (key.indexOf('@') > -1) { //自定义事件
                renderStr += `"${key.replace('@','')}": ${val},`
            } else { //其它属性
                renderStr += `"${key}": "${val}",`
            }
        }
        renderStr += '}'
        return renderStr
    }
}

function parseExp(exp) {
    //存在=判断为表达式，否则是变量
    if(exp === '' || isUndef(exp)){
        return null
    }else if (exp.indexOf('=') > -1) {
        return '(function(){ return ' + exp + ' })()'
    } else {
        return exp
    }

}

const isBuiltInDirective = makeMap(
    'v-text,v-html,v-if,v-else,v-else-if,v-for,v-model,v-slot'
)


const cacheObj = {} //以vm.uid为唯一ID缓存render函数
export function compileToFunctions(vm, template) {
    let fn
    if (cacheObj[vm._uid]) {
        fn = cacheObj[vm._uid]
    } else {
        let renderStr = render(template)
        fn = new Function("e", "with(e){  return " + renderStr + "   }")
        cacheObj[vm._uid] = fn
    }
    return {
        render: fn
    }
}

